home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Programming Sound Cards
/
Programming Sound Cards.iso
/
sound_65
/
sounddev.cpp
< prev
Wrap
C/C++ Source or Header
|
1995-01-01
|
8KB
|
239 lines
// SoundDev.cpp
// General sound functions that can be applied to derived sound classes
// (eg SbDevice)
// Written by Christopher M. Box (1993)
#include <conio.h>
#include <io.h>
#include <process.h>
#include <values.h>
#include "sndclass.h"
#define DEFAULTRECORDLEN 10000000L // 10 megabytes
// Forward function definitions
static void bar_display(char val);
static void bar_display(int val);
// Function: setsizes
// Given a buffer size ('bufsize') and remaining file length ('len'), this
// determines lengths for the lower and upper half-buffers. It is used four
// times in the file_dma function, so it is brought out here as an inline
// function.
inline void SoundDevice::setsizes(unsigned &bufsize, long &len,
unsigned &lo_sz, unsigned &hi_sz) {
len -= (lo_sz = (bufsize < len) ? bufsize : (unsigned) len);
len -= (hi_sz = (bufsize < len) ? bufsize : (unsigned) len);
}
// Function: file_dma
// Handles the running of buffered DMA from a sound device to a disk file,
// or in the opposite direction. 'handle' is the handle of a file opened
// with _open(), 'lobuf' is a pointer to the bottom of the single DMA buffer,
// 'bufsize' is the size of it (up to a maximum of 64K), 'len' is the desired
// length of file to play (or zero for the full length) and 'dir' sets the
// direction (recording or playback).
void SoundDevice::file_dma(int handle, byte far *lobuf, unsigned bufsize,
long len, byte dir) {
reset();
byte far *hibuf = lobuf + bufsize; // Calculate pos of high half-buffer
unsigned lo_sz, hi_sz, cur_sz;
dt_min=65535U; // Initialise disk time statistics
dt_max=0;
if (dir == RECORD) {
if (len == 0) len = DEFAULTRECORDLEN;
setsizes(bufsize,len,lo_sz,hi_sz);
buf_dma_start(lobuf,lo_sz+hi_sz,RECORD);
// Wait for the transfer to start to avoid confusing buf_dma_lo().
while (dma_addr() == 0);
while (1) {
if (lo_sz == 0) break;
if (buf_dma_lo(lo_sz)) { // if terminated by keypress
lo_sz = dma_addr();
Dprint(("Saving up to position %u.\r\n",lo_sz));
hi_sz = 0;
}
if (disk_io(handle,lobuf,lo_sz,RECORD)) return;
if (hi_sz == 0) break;
cur_sz = hi_sz;
setsizes(bufsize,len,lo_sz,hi_sz);
if (buf_dma_hi(cur_sz,lo_sz+hi_sz)) {
cur_sz = dma_addr() - bufsize;
Dprint(("Saving hibuf, length %u.\r\n",cur_sz));
lo_sz = 0;
}
if (disk_io(handle,hibuf,cur_sz,RECORD)) return;
}
} else { // Play function
if (len == 0) len = filelength(handle);
setsizes(bufsize,len,lo_sz,hi_sz);
if (lo_sz == 0) {
cprintf("Error: zero length.\r\n");
exit(-1);
}
if (_read(handle,lobuf,lo_sz) != lo_sz) { // Fill first buffer
cprintf("File read error.\r\n");
exit(-1);
}
buf_dma_start(lobuf,lo_sz+hi_sz,PLAY);
while (dma_addr() == 0); // Wait for it to start
while (1) { // Main buffer loop - forever
if (hi_sz == 0) {
buf_dma_lo(lo_sz);
break;
} else {
if (disk_io(handle,hibuf,hi_sz,PLAY)) return;
if (buf_dma_lo(lo_sz)) break;
}
if (len == 0) {
buf_dma_hi(hi_sz, 0);
break;
} else {
setsizes(bufsize,len,lo_sz,hi_sz);
if (disk_io(handle,lobuf,lo_sz,PLAY)) return;
if (buf_dma_hi(bufsize, lo_sz+hi_sz)) break;
}
}
}
// At the end print out min and max times per disk access,
// in terms of both bytes and milliseconds.
unsigned bytes_per_ms =
(unsigned) (((long) get_rate() * get_width() + 500) / 1000);
cprintf("Disk stats: min %u (%ums), max %u (%ums).\r\n",
dt_min,
dt_min / bytes_per_ms,
dt_max,
dt_max / bytes_per_ms);
}
// Function: disk_io
// Takes care of the time-consuming disk reads/writes of file 'handle'
// from/into buffer 'buf', of 'len' bytes in direction 'dir'.
// Automatically measures the time taken in terms of the number of bytes
// advanced by the DMA and updates the min/max times.
int SoundDevice::disk_io(int handle, byte far *buf, unsigned len, byte dir) {
unsigned old_addr = dma_addr();
if (dir == RECORD) {
if (_write(handle,buf,len) != len) {
cprintf("Write error: disk full?\r\n");
return 1;
}
} else {
if (_read(handle,buf,len) != len) {
cprintf("File read error.\r\n");
return 1;
}
}
unsigned new_addr = dma_addr();
unsigned disktime = new_addr - old_addr;
if (new_addr < old_addr) {
Dprint(("Long disk wait. ")); // Overflow - can't measure the time
} else {
if (disktime && disktime<dt_min) dt_min = disktime;
if (disktime>dt_max) dt_max = disktime;
Dprint(("DT = %X. ",disktime));
}
return 0;
}
// Function: max
// Defined both for integers and bytes, for use in monitor_input
inline int max(int v1, int v2) {
return (v1 > v2 ? v1 : v2);
}
inline byte max(byte v1, byte v2) {
return (v1 > v2 ? v1 : v2);
}
// Function: monitor_input
// Displays input levels by continously reading in DMA for 'blocklen' bytes
// and showing the maximum value within the block as a bar graph from left
// to right. 80 columns represents full scale. The value chosen for
// 'blocklen' affects the responsiveness and accuracy of the bar graph.
// I have included code for both the SB (unsigned mono bytes) data format
// and the CD (signed stereo words) format to demonstrate how both types can
// be used.
void SoundDevice::monitor_input(byte far *dmabuf, unsigned blocklen) {
if (blocklen > 65000) {
cprintf("Use a smaller block length.\r\n");
return;
}
blocklen &= ~3; // Round down to a double-word boundary
byte maxbyte;
int maxleft, maxright;
byte far *bdata; // Different pointer types for SB (byte)
int far *idata; // and CD (signed int)
byte far *data_end = dmabuf + blocklen;
clrscr(); // Start by clearing the screen and disabling the cursor
_setcursortype(_NOCURSOR);
while (1) {
buf_dma_start(dmabuf,65500U,RECORD);
while ((dma_addr() < blocklen) && !kbhit());// Wait for buffer to fill
halt(); // Prevent further DMA writes
reset();
if (kbhit()) break;
maxbyte = 0; // Initialise max variables
maxleft = maxright = -MAXINT;
switch(identify()) { // Execute different loops depending on data type
case SB_ID:
for (bdata = dmabuf; bdata < data_end; bdata++) {
maxbyte = max(maxbyte, *bdata);
}
break;
case CD_ID:
for (idata = (int *) dmabuf; idata < (int *) data_end; idata++) {
maxleft = max(maxleft, *idata);
idata++;
maxright = max(maxright, *idata);
}
break;
}
gotoxy(1,1); // Move invisible cursor to top left
switch (identify()) {
case SB_ID: // Subtract 0x80 before displaying
bar_display((char) (maxbyte - 128));
break;
case CD_ID:
bar_display(maxleft);
bar_display(maxright);
break;
}
}
getch(); // Remove character from keyboard buffer
_setcursortype(_NORMALCURSOR); // Restore cursor
return;
}
// Function: bar_display (signed byte)
// Converts byte (0 to 7F) to int and calls the real bar_display function
static void bar_display(char val) {
int ival = val * (MAXINT / 127);
bar_display(ival);
}
// Function: bar_display (int)
// Prints a row of asterisks corresponding to the size of 'val'. Always
// completes the line with spaces to eliminate any previous characters.
static void bar_display(int val) {
if (val < 0) val = 0;
int bar_len = val / (MAXINT / 80);
int i;
for (i = 0; i < 80; i++) {
putch(i <= bar_len ? '*' : ' ');
}
return;
}